package com.yyge.config.security.filter;


import com.yyge.config.Constants;
import com.yyge.config.security.dto.SecurityUser;
import com.yyge.config.security.login.AuthEntryPoint;
import com.yyge.config.security.service.impl.UserDetailsServiceImpl;
import com.yyge.entity.Role;
import com.yyge.entity.User;
import com.yyge.utils.SecurityRequest;
import com.yyge.utils.SecurityResponse;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

;

/**
 * <p> 访问鉴权 - 每次访问接口都会经过此 </p>
 *
 * @author : gityyge
 * @description :
 * @date : 2019/10/12 16:17
 */
@Slf4j
@Component
public class AuthFilter extends OncePerRequestFilter {

    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxFileSize;

    @Value("${spring.servlet.multipart.max-request-size}")
    private String maxRequestSize;

    @Autowired
    AuthEntryPoint authenticationEntryPoint;

    private final UserDetailsServiceImpl userDetailsService;

    protected AuthFilter(UserDetailsServiceImpl userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("========请求头类型========： " + request.getContentType());

        SecurityRequest wrappedRequest = null;
        SecurityResponse wrappedResponse = null;
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try {

//            SecurityContext context = SecurityContextHolder.getContext();
//            if (context.getAuthentication() != null && context.getAuthentication().isAuthenticated()) {
//                filterChain.doFilter(wrappedRequest, wrappedResponse);
//                return;
//            }

            // 前后端分离情况下，前端登录后将token储存在cookie中，每次访问接口时通过token去拿用户权限
            //String jwtToken = request.getHeader(Constants.REQUEST_HEADER);
            String jwtToken = request.getHeader("Authorization");

            if ( jwtToken == null || jwtToken.isEmpty()) {
                 jwtToken = request.getParameter("t");
            }
            log.debug("后台检查令牌:{}", jwtToken);
            if ((jwtToken!=null && !jwtToken.isEmpty()) && !"undefined".equals(jwtToken)) {
                // JWT相关start ===========================================
                // 获取jwt中的信息
//                Claims claims = Jwts.parser().setSigningKey(Constants.SALT).parseClaimsJws(jwtToken.replace("Bearer", "")).getBody();
                // 获取当前登录用户名
//                System.out.println("获取当前登录用户名: " + claims.getSubject());
                // TODO 如需使用jwt特性在此做处理~
                // JWT相关end ===========================================

                // 检查token
                SecurityUser securityUser = userDetailsService.getUserByToken(jwtToken);
                if (securityUser == null || securityUser.getCurrentUserInfo() == null) {
                    log.error("TOKEN已过期，请重新登录！{}",jwtToken);
                    throw new BadCredentialsException("TOKEN已过期，请重新登录！");
                }
                User currentUserInfo = securityUser.getCurrentUserInfo();
                if (currentUserInfo.getFlag().equals("0")) {
                    log.error("TOKEN已过期，请重新登录！{}",jwtToken);
                    throw new BadCredentialsException("TOKEN已过期，请重新登录！");
                }
                List<Role> roleList = securityUser.getRoleList();
                for (Role role : roleList) {
                    if(role.getType().equals(Constants.ROLE_LOGIN_CUSTOMER)){
                        log.error("TOKEN已过期，请重新登录！{}",jwtToken);
                        throw new BadCredentialsException("TOKEN已过期，请重新登录！");
                    }
                }

                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities());
                // 全局注入角色权限信息和登录用户基本信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
            /**
             * 请求头必须是application/json才能正常请求，
             * 如果不是json，就不通过token拿到用户，就会报401权限错误
             * 把ContentType的application/json限制去掉
             */
            if ((request.getContentType() == null && request.getContentLength() > 0)
                    || (request.getContentType() != null && !request.getContentType().contains(Constants.REQUEST_HEADERS_CONTENT_TYPE))) {
                filterChain.doFilter(request, response);
                return;
            }
            wrappedRequest = new SecurityRequest(request);
            wrappedResponse = new SecurityResponse(response);
            // 记录请求的消息体
            logRequestBody(wrappedRequest);
            // 从这里才进入Controller
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } catch (ExpiredJwtException e) {
            // jwt令牌过期
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest, response, null);
        } catch (AuthenticationException e) {
            SecurityContextHolder.clearContext();
            this.authenticationEntryPoint.commence(wrappedRequest, response, e);
        } finally {
            stopWatch.stop();
            long usedTimes = stopWatch.getTotalTimeMillis();
            // 记录响应的消息体
            logResponseBody(wrappedRequest, wrappedResponse, usedTimes);
        }

    }

    private String logRequestBody(SecurityRequest request) {
        SecurityRequest wrapper = request;
        if (wrapper != null) {
            try {
                String bodyJson = wrapper.getBodyJsonStrByJson(request);
                String url = wrapper.getRequestURI().replace("//", "/");
                System.out.println("-------------------------------- 请求url: " + url + " --------------------------------");
                Constants.URL_MAPPING_MAP.put(url, url);
                log.info("`{}` 接收到的参数: {}", url, bodyJson);
                return bodyJson;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void logResponseBody(SecurityRequest request, SecurityResponse response, long useTime) {
//        SecurityResponse wrapper = response;
//        if (wrapper != null) {
//            byte[] buf = wrapper.getBody();
//            if (buf.length > 0) {
//                String payload;
//                try {
//                    payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
//                } catch (UnsupportedEncodingException ex) {
//                    payload = "[unknown]";
//                }
//                log.info("`{}`  耗时:{}ms  返回的参数: {}", Constants.URL_MAPPING_MAP.get(request.getRequestURI()), useTime, payload);
//            }
//        }
    }

}
